package com.example.petplanet.service;



import com.example.petplanet.config.AliyunConfig;
import com.example.petplanet.domain.*;
import com.example.petplanet.dto.CallBackDTO;
import com.example.petplanet.mapper.*;
import com.example.petplanet.result.Result;
import com.example.petplanet.util.OSSUtil;
import com.example.petplanet.util.TLUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.type.TypeReference; // ✅ 正确


import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
public class ContentService {
    //作为缓存 记得及时清楚冗余数据 userid也应该用缓存
    public static final ConcurrentHashMap<String, List<CallBackDTO>> callbackMap = new ConcurrentHashMap<>();
    private final ContentDao contentDao;
    private final AliyunConfig aliyunConfig;
    private final ShareService shareService;
    private final StringRedisTemplate stringRedisTemplate;
    private final LikeContentDao likeContentDao;
    private final MarkBookDao markBookDao;
    private final CommentDao commentDao;
    private final LikeCommentDao likeCommentDao;
    private final ObjectMapper objectMapper;

    private final String SYNC_LOCK = "isSyncing";


    private final RestTemplate restTemplate;

    private final RedissonClient redissonClient;

    @Autowired
    ContentService(ContentDao contentDao, AliyunConfig aliyunConfig, StringRedisTemplate stringRedisTemplate, MarkBookDao markBookDao, LikeContentDao likeContentDao, ShareService shareService, CommentDao commentDao, LikeCommentDao likeCommentDao, RestTemplate restTemplate, RedissonClient redissonClient, ObjectMapper objectMapper){
        this.contentDao=contentDao;
        this.aliyunConfig=aliyunConfig;
        this.stringRedisTemplate = stringRedisTemplate;
        this.markBookDao = markBookDao;
        this.likeContentDao = likeContentDao;
        this.shareService = shareService;
        this.commentDao = commentDao;
        this.likeCommentDao = likeCommentDao;
        this.restTemplate = restTemplate;
        this.redissonClient = redissonClient;
        this.objectMapper = objectMapper;
    }


    public Result<Object> CallBack(CallBackDTO callBackDTO){

        try {
            String userId=callBackDTO.getUserId();
            String uploadId=callBackDTO.getUploadId();
            String key=userId+'-'+uploadId;

            //这里笔记
            List<CallBackDTO> list = callbackMap.computeIfAbsent(key, k -> new ArrayList<>());
            list.add(callBackDTO);
        } catch (NumberFormatException e) {
            return Result.error("在缓存已上传的文件信息时出错了: "+callBackDTO);
        }
        return Result.success("此文件信息记录成功: "+callBackDTO);
    }




    public Result<Object> getRecommendContents() throws JsonProcessingException {
        String url = "http://114.55.247.90:5002/recommend?user_id=%s";
//        String final_url=String.format(url,TLUtil.getUser().getUserId());
        String final_url=String.format(url,5180288385893779L);
        ResponseEntity<String> result=restTemplate.getForEntity(final_url,String.class);

        Map<String,List<Long>> map_content_id;
        List<Long> all_content_id;

        System.out.println(result.getBody());

        map_content_id = objectMapper.readValue(result.getBody(), new TypeReference<>() {});
// all_content_id = objectMapper.readValue(result.getBody(), new TypeReference<List<Long>>() {});
        all_content_id=map_content_id.get("recommendations");

//        List<Content> contents=new ArrayList<>();

//        for (long content_id:all_content_id){
////            contents.add(cacheCp.queryWithPassThrough("content",content_id,Content.class,2L,TimeUnit.DAYS,contentDao::getContent));
//        }
        return Result.success(all_content_id);
    }


    public Result<Object> getAllContent() {
        long userId=TLUtil.getUserId();
        List<Content> contents=contentDao.getAllContent(userId);
        contents=updateContentsGoodFromMysql(contents);
        if (contents==null) return Result.error("获取失败，请重试");
        return Result.success(contents);
    }

    public Result<Object> getMyContents(){
        //处理将未公布的视频排序
        List<Content> contents=contentDao.getMyContents(TLUtil.getUserId());
        contents=updateContentsGoodFromMysql(contents);
        if (contents==null) return Result.error("获取失败，请重试");
        return Result.success(contents);
    }

    public List<Content> updateContentsGoodFromMysql(List<Content> contents){
        for (Content content : contents) {
            // 构造Redis中的键
            String key = "app:likeCount:content:" + content.getContentId();
            // 检查Redis中是否存在该键
            Boolean exists = stringRedisTemplate.hasKey(key);
            if(exists){
                Long goodCount = Long.parseLong(stringRedisTemplate.opsForValue().get(key));
                content.setGood(goodCount);
            }
        }
        return contents;
    }

    public Result<Object> editContent(Long contentId,String description,boolean permission){
        //判断属于
        if(contentDao.editContent(TLUtil.getUserId(), contentId,description,permission)==0) return Result.error("更新失败，请重新试");
        return Result.success();
    }

    @Transactional
    public Result<Object> deleteFileByObjectName(String objectName){
        try{
            if(contentDao.deleteFileByObjectName(objectName)==0) throw new RuntimeException("数据库层删除发布内容的文件出错");
            OSSUtil.deleteFileFromAli(aliyunConfig,objectName);
            return  Result.success();
        } catch (Exception e) {
            return Result.error("删除失败: "+e.getMessage());
        }
    }


    //评论也需要改，同时考虑需不需要加入等待时间，以及考虑删除操作正在进行，同步上锁的情况
    //保证删除操作也能多线程处理
    @Transactional
    public Result<Object> deleteContent(Long contentId){
        // 检查是否处于禁止操作时间
        if (shareService.isForbiddenDelTime()) {
            return Result.error("当前处于禁止操作时间，请等待至少1分钟后再尝试");
        }
        if(shareService.isSyncLockCheckTime()){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return Result.error("删除操作异常: " + e.getMessage());
            }
            RLock syncLock = redissonClient.getLock(SYNC_LOCK);
            if (syncLock.isLocked()) {
                return Result.error("同步操作正在进行，删除操作失败");
            }
        }
        //帖子点赞，收藏，以及本人的获赞，获收藏数都要更新
        //删除附属所有评论
        //已同步到mysql要更新，未同步到mysql的redis也要更新
        try {
            //获取图片objectName
            List<String> objectNames = contentDao.selectFileNames(contentId);
            //获取评论
            Set<Long> commentIds = commentDao.getCommentIdsFromContent(contentId);
            List<LikeComment> likeComments = new ArrayList<>();
            if(!commentIds.isEmpty()){
                likeComments=likeCommentDao.selectLikeComByIds(commentIds);
                if(likeComments == null) likeComments=new ArrayList<>();
            }
            //点赞
            List<LikeContent> likeContents = likeContentDao.selectLikeContentById(contentId);
            if(likeContents == null) likeContents=new ArrayList<>();
            //MYSQL级联删除
            if(contentDao.deleteContent(contentId)==0) throw new RuntimeException("数据库层删除发布内容出错");
            //删除图片
            if(!objectNames.isEmpty()){
                for(String objectName : objectNames) OSSUtil.deleteFileFromAli(aliyunConfig,objectName);
            }
            //redis同步
            String key="app:content:"+contentId;
            stringRedisTemplate.delete(key);
            //收藏
            shareService.addToDeleteMarkBookIds(contentId);
            //评论同步(量级增大后，分批处理，mysql的获取也是)
            if(!commentIds.isEmpty()) {
                shareService.deleteCommentRedis(likeComments,commentIds);
            }
            //点赞帖子同步，user信息同步
            deleteContentRedis(contentId,likeContents);
            //用户的获赞数和获收藏数，同步时再删除
            shareService.addToUpdateLikeMarkUsers(TLUtil.getUserId());
            return Result.success();
        }catch (Exception e){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//还未测试
            return Result.error("删除失败: "+e.getMessage());
        }
    }


    private void deleteContentRedis(Long contentId, List<LikeContent> likeContents){
        //删除同步数据
        for (LikeContent likeContent:likeContents){
            Long userId = likeContent.getUserId();
            if(!shareService.judgeUserExistsRedis(userId)) continue;
            User user = shareService.getUserByIdRedis(userId);
            user.getLikeContents().remove(contentId);
            shareService.updateUserRedis(user);
        }
        //删除未同步数据
        String countKey="app:likeCount:content:" + contentId.toString();
        if(stringRedisTemplate.hasKey(countKey)){
            stringRedisTemplate.delete(countKey);
            Set<String> NoSYncKey = stringRedisTemplate.keys("app:like:content:*:"+ contentId+":*");
            if (!NoSYncKey.isEmpty())  {
                for (String key : NoSYncKey){
                    String[] parts = key.split(":");
                    Long noSyncUserId = Long.parseLong(parts[3]);
                    User noSyncUser = shareService.getUserByIdRedis(noSyncUserId);
                    noSyncUser.getLikeContents().remove(contentId);
                    shareService.updateUserRedis(noSyncUser);
                    stringRedisTemplate.delete(key);
                }
            }
        }
    }

    @Transactional
    public Result<Object> launchContent(Content content,String uploadId,int total){
        try {
            Long userId= TLUtil.getUserId();
            List<CallBackDTO> list=callbackMap.get(userId.toString()+'-'+uploadId);

            if (list.size()==0) throw new RuntimeException("上传文件预计问题导致回调失败");
            else if (list.size()<total) throw new RuntimeException("有文件少传了");
            else if(list.size()>total) throw new RuntimeException("缓存中的文件数据意外增多了");

            saveText(userId,content);
            Long contentId=contentDao.getContentId();

            saveFile(list,contentId,uploadId);

            callbackMap.remove(userId.toString()+'-'+uploadId);
            return Result.success();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//还未测试
            return Result.error("发布失败"+e.getMessage());
        }
    }
    public void saveText(Long userId, Content content)throws RuntimeException{
        if(contentDao.saveText(userId,content)==0) throw new RuntimeException("存储发布内容的文本出错");
    }
    public void saveFile(List<CallBackDTO> list,Long contentId,String uploadId) throws RuntimeException{
        if(contentDao.saveFile(list,contentId,uploadId)==0) throw new RuntimeException("存储发布内容的文件出错");
    }

    public Result<Object> getContentById(Long contentId) {
        Content content=contentDao.getContentById(contentId);
        if(content==null) return Result.error("获取失败");
        return  Result.success(content);
    }

    public Result<Object> markBook(Long contentId) {
        //获取targetId
        Long targetId=shareService.getTargetId(contentId);
        if(targetId==null) return Result.error("收藏失败");
        //收藏人，延时
        User user=TLUtil.getUser();
        Long userId=TLUtil.getUserId();
        boolean addition = user.getMarkBooks().add(contentId);
        if(!addition) return Result.error("已收藏");
        shareService.updateUserRedis(user);
        String key="app:user:"+userId.toString();
        stringRedisTemplate.expire(key,10, TimeUnit.DAYS);
        //被收藏的人
        User targetUser = shareService.getUserByIdRedis(targetId);
        if(targetUser ==null){
            //MYSQL
            if(markBookDao.markBook(userId, contentId,targetId) ==0) return Result.error("收藏失败");
            return Result.success();
        }
        targetUser.setUserTotalBookmarkCount(targetUser.getUserTotalBookmarkCount()+1);
        shareService.updateUserRedis(targetUser);
        return Result.success();
    }

    public Result<Object> cancelMarkBook(Long contentId) {
        //获取targetId
        Long targetId=shareService.getTargetId(contentId);
        if(targetId==null) return Result.error("收藏失败");
        //取消收藏人，延时
        User user=TLUtil.getUser();
        Long userId=TLUtil.getUserId();
        boolean remove = user.getMarkBooks().remove(contentId);
        if(!remove) return Result.error("未收藏");
        shareService.updateUserRedis(user);
        String key="app:user:"+userId.toString();
        stringRedisTemplate.expire(key,10,TimeUnit.DAYS);
        //被取消收藏人
        User targetUser = shareService.getUserByIdRedis(targetId);
        if(targetUser ==null){
            //MYSQL
            if(markBookDao.cancelMarkBook(userId,contentId) ==0) {
                //回滚Redis
                return Result.error("取消失败");
            }
            return Result.success();
        }
        targetUser.setUserTotalBookmarkCount(targetUser.getUserTotalBookmarkCount()-1);
        shareService.updateUserRedis(targetUser);
        return Result.success();
    }



    public Result<Object> likeContent(Long contentId) {
        return toggleLikeContent(contentId,true);
    }

    public Result<Object> cancelLikeContent(Long contentId) {
        return toggleLikeContent(contentId,false);
    }

    public Result<Object> toggleLikeContent(Long contentId, boolean isLike) {
        Long targetId = shareService.getTargetId(contentId);
        if (targetId == null) return Result.error("点赞失败");
        User user = TLUtil.getUser();
        Long userId = TLUtil.getUserId();
        Set<Long> likeContents = user.getLikeContents();
        boolean success = isLike ? likeContents.add(contentId) : likeContents.remove(contentId);
        if (!success) return Result.error(isLike ? "已点赞" : "还未点赞");
        shareService.updateUserRedis(user);
        String key = "app:user:" + userId.toString();
        stringRedisTemplate.expire(key, 10, TimeUnit.DAYS);
        String contentKey = "app:like:content:" + userId + ":" + contentId + ":" + targetId;
        boolean updated = shareService.updateKeyValue(contentKey, isLike?"true":"false", 7, TimeUnit.HOURS);
        if (!updated) {
            return Result.error(isLike ? "已点赞" : "未点赞");
        }
        User targetUser = shareService.getUserByIdRedis(targetId);
        updateContentLikeCount(contentId, isLike ? 1L : -1L, 7, TimeUnit.HOURS);
        if (targetUser == null) {
            // 操作likeContent表
            if(isLike){
                if(likeContentDao.insert(userId,contentId,targetId,true)==0) return Result.error("点赞失败");
            }else {
                if(likeContentDao.cancelLike(userId,contentId)==0) return Result.error("取消点赞失败");
            }
            return Result.success();
        }
        targetUser.setUserLikes(targetUser.getUserLikes() + (isLike ? 1 : -1));
        shareService.updateUserRedis(targetUser);
        return Result.success();
    }


    private void updateContentLikeCount(Long contentId,Long value,long timeout,TimeUnit timeUnit){
        //没有key，新增，有key加value
        String key="app:likeCount:content:"+contentId.toString();
        Boolean exists = stringRedisTemplate.hasKey(key);
        if (!exists) {
            // 如果键不存在，直接设置值
            Content content=contentDao.getContentById(contentId);
            Long good = content.getGood();
            good=good+value;
            stringRedisTemplate.opsForValue().set(key,
                    good.toString(),
                    timeout,
                    timeUnit);
        }else {
            // 如果键存在，使用 INCRBY 命令更新当前值
            stringRedisTemplate.opsForValue().increment(key, value);
            stringRedisTemplate.expire(key,timeout,timeUnit);
        }
    }
}
